package top.codedance.iotp.client.plugins.sync.http;

import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

public class HttpRequestUtil {
    public static String post(String protocol, String url, String jsonStr, JSONObject headers) {
        HttpPost httpPost = new HttpPost(protocol + ":" + url);
        if (jsonStr != null) {
            StringEntity entity = new StringEntity(jsonStr, StandardCharsets.UTF_8);
            httpPost.setEntity(entity);
        }
        if(headers != null) {
            Set<Map.Entry<String, Object>> entries = headers.entrySet();
            for (Map.Entry<String, Object> item : entries) {
                httpPost.setHeader(item.getKey(), (String) item.getValue());
            }
        }
        return execute(protocol, httpPost);
    }

    public static String get(String protocol, String url, JSONObject headers) {
        HttpGet httpGet = new HttpGet(protocol + ":" + url);
        if(headers != null) {
            Set<Map.Entry<String, Object>> entries = headers.entrySet();
            for (Map.Entry<String, Object> item : entries) {
                httpGet.setHeader(item.getKey(), (String) item.getValue());
            }
        }
        return execute(protocol, httpGet);
    }

    public static String put(String protocol, String url, String jsonStr, JSONObject headers) {
        HttpPut httpPut = new HttpPut(protocol + ":" + url);
        if (jsonStr != null) {
            StringEntity entity = new StringEntity(jsonStr, StandardCharsets.UTF_8);
            httpPut.setEntity(entity);
        }
        if(headers != null) {
            Set<Map.Entry<String, Object>> entries = headers.entrySet();
            for (Map.Entry<String, Object> item : entries) {
                httpPut.setHeader(item.getKey(), (String) item.getValue());
            }
        }
        return execute(protocol, httpPut);
    }

    public static String delete(String protocol, String url, JSONObject headers) {
        HttpDelete httpDelete = new HttpDelete(protocol + ":" + url);
        if(headers != null) {
            Set<Map.Entry<String, Object>> entries = headers.entrySet();
            for (Map.Entry<String, Object> item : entries) {
                httpDelete.setHeader(item.getKey(), (String) item.getValue());
            }
        }
        return execute(protocol, httpDelete);
    }

    public static String head(String protocol, String url, JSONObject headers) {
        HttpHead httpHead = new HttpHead(protocol + ":" + url);
        if(headers != null) {
            Set<Map.Entry<String, Object>> entries = headers.entrySet();
            for (Map.Entry<String, Object> item : entries) {
                httpHead.setHeader(item.getKey(), (String) item.getValue());
            }
        }
        return execute(protocol, httpHead);
    }

    public static String options(String protocol, String url, JSONObject headers) {
        HttpOptions httpOptions = new HttpOptions(protocol + ":" + url);
        if(headers != null) {
            Set<Map.Entry<String, Object>> entries = headers.entrySet();
            for (Map.Entry<String, Object> item : entries) {
                httpOptions.setHeader(item.getKey(), (String) item.getValue());
            }
        }
        return execute(protocol, httpOptions);
    }

    public static String patch(String protocol, String url, String jsonStr, JSONObject headers) {
        HttpPatch httpPatch = new HttpPatch(protocol + ":" + url);
        if (jsonStr != null) {
            StringEntity entity = new StringEntity(jsonStr, StandardCharsets.UTF_8);
            httpPatch.setEntity(entity);
        }
        if(headers != null) {
            Set<Map.Entry<String, Object>> entries = headers.entrySet();
            for (Map.Entry<String, Object> item : entries) {
                httpPatch.setHeader(item.getKey(), (String) item.getValue());
            }
        }
        return execute(protocol, httpPatch);
    }

    private static String execute(String protocol, HttpUriRequest request) {
        HttpClient httpClient;
        if(protocol.toUpperCase().equals("HTTPS")){
            httpClient = sslClient();
        }else{
            httpClient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build();
        }
        if(httpClient != null) {
            try {
                HttpResponse response = httpClient.execute(request);
                HttpEntity responseEntity = response.getEntity();
                StatusLine statusLine = response.getStatusLine();
                if (responseEntity != null && statusLine.getStatusCode() == 200) {
                    BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8));
                    StringBuffer sb = new StringBuffer("");
                    String line = "";
                    while ((line = in.readLine()) != null) {
                        sb.append(line);
                    }
                    in.close();
                    String content = sb.toString();
                    return content;
                } else {
                    return "未正常返回, 状态码：" + statusLine.getStatusCode();
                }
            } catch (Exception e) {
                return e.getMessage();
            }
        }else{
            return "Http client create fail.";
        }
    }

    private static HttpClient sslClient() {
        try {
            // 在调用SSL之前需要重写验证方法，取消检测SSL
            X509TrustManager trustManager = new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                public void checkClientTrusted(X509Certificate[] xcs, String str) {
                }

                public void checkServerTrusted(X509Certificate[] xcs, String str) {
                }
            };
            SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
            ctx.init(null, new TrustManager[]{trustManager}, null);
            SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
            // 创建Registry
            RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT)
                    .setExpectContinueEnabled(Boolean.TRUE).setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST))
                    .setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC)).build();
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", socketFactory).build();
            // 创建ConnectionManager，添加Connection配置信息
            PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            CloseableHttpClient closeableHttpClient = HttpClients.custom().setConnectionManager(connectionManager)
                    .setDefaultRequestConfig(requestConfig).setRedirectStrategy(new LaxRedirectStrategy()).build();
            return closeableHttpClient;
        } catch (KeyManagementException ex) {
            throw new RuntimeException(ex);
        } catch (NoSuchAlgorithmException ex) {
            throw new RuntimeException(ex);
        }
    }
}
